home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / redakcyjne / programy / VideoLAN Client (VLC) 1.0.5 / vlc-1.0.5-win32.exe / lua / intf / rc.lua < prev    next >
Text File  |  2010-01-30  |  25KB  |  658 lines

  1. --[==========================================================================[
  2.  rc.lua: remote control module for VLC
  3. --[==========================================================================[
  4.  Copyright (C) 2007-2009 the VideoLAN team
  5.  $Id$
  6.  
  7.  Authors: Antoine Cellerier <dionoea at videolan dot org>
  8.  
  9.  This program is free software; you can redistribute it and/or modify
  10.  it under the terms of the GNU General Public License as published by
  11.  the Free Software Foundation; either version 2 of the License, or
  12.  (at your option) any later version.
  13.  
  14.  This program is distributed in the hope that it will be useful,
  15.  but WITHOUT ANY WARRANTY; without even the implied warranty of
  16.  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  17.  GNU General Public License for more details.
  18.  
  19.  You should have received a copy of the GNU General Public License
  20.  along with this program; if not, write to the Free Software
  21.  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
  22. --]==========================================================================]
  23.  
  24. description=
  25. [============================================================================[
  26.  Remote control interface for VLC
  27.  
  28.  This is a modules/control/rc.c look alike (with a bunch of new features)
  29.  
  30.  Use on local term:
  31.     vlc -I luarc
  32.  Use on tcp connection:
  33.     vlc -I luarc --lua-config "rc={host='localhost:4212'}"
  34.  Use on multiple hosts (term + 2 tcp ports):
  35.     vlc -I luarc --lua-config "rc={hosts={'*console','localhost:4212','localhost:5678'}}"
  36.  
  37.  Note:
  38.     -I luarc is an alias for -I lua --lua-intf rc
  39.  
  40.  Configuration options setable throught the --lua-config option are:
  41.     * hosts: A list of hosts to listen on.
  42.     * host: A host to listen on. (won't be used if `hosts' is set)
  43.  The following can be set using the --lua-config option or in the interface
  44.  itself using the `set' command:
  45.     * prompt: The prompt.
  46.     * welcome: The welcome message.
  47.     * width: The default terminal width (used to format text).
  48.     * autocompletion: When issuing an unknown command, print a list of
  49.                       possible commands to autocomplete with. (0 to disable,
  50.                       1 to enable).
  51.     * autoalias: If autocompletion returns only one possibility, use it
  52.                  (0 to disable, 1 to enable).
  53.     * flatplaylist: 0 to disable, 1 to enable.
  54. ]============================================================================]
  55.  
  56. require("common")
  57. skip = common.skip
  58. skip2 = function(foo) return skip(skip(foo)) end
  59. setarg = common.setarg
  60. strip = common.strip
  61.  
  62. --[[ Setup default environement ]]
  63. env = { prompt = "> ";
  64.         width = 70;
  65.         autocompletion = 1;
  66.         autoalias = 1;
  67.         welcome = "Remote control interface initialized. Type `help' for help.";
  68.         flatplaylist = 0;
  69.       }
  70.  
  71. --[[ Import custom environement variables from the command line config (if possible) ]]
  72. for k,v in pairs(env) do
  73.     if config[k] then
  74.         if type(env[k]) == type(config[k]) then
  75.             env[k] = config[k]
  76.             vlc.msg.dbg("set environement variable `"..k.."' to "..tostring(env[k]))
  77.         else
  78.             vlc.msg.err("environement variable `"..k.."' should be of type "..type(env[k])..". config value will be discarded.")
  79.         end
  80.     end
  81. end
  82.  
  83. --[[ Command functions ]]
  84. function set_env(name,client,value)
  85.     if value then
  86.         local var,val = split_input(value)
  87.         if val then
  88.             s = string.gsub(val,"\"(.*)\"","%1")
  89.             if type(client.env[var])==type(1) then
  90.                 client.env[var] = tonumber(s)
  91.             else
  92.                 client.env[var] = s
  93.             end
  94.         else
  95.             client:append( tostring(client.env[var]) )
  96.         end
  97.     else
  98.         for e,v in common.pairs_sorted(client.env) do
  99.             client:append(e.."="..v)
  100.         end
  101.     end
  102. end
  103.  
  104. function save_env(name,client,value)
  105.     env = common.table_copy(client.env)
  106. end
  107.  
  108. function alias(client,value)
  109.     if value then
  110.         local var,val = split_input(value)
  111.         if commands[var] and type(commands[var]) ~= type("") then
  112.             client:append("Error: cannot use a primary command as an alias name")
  113.         else
  114.             if commands[val] then
  115.                 commands[var]=val
  116.             else
  117.                 client:append("Error: unknown primary command `"..val.."'.")
  118.             end
  119.         end
  120.     else
  121.         for c,v in common.pairs_sorted(commands) do
  122.             if type(v)==type("") then
  123.                 client:append(c.."="..v)
  124.             end
  125.         end
  126.     end
  127. end
  128.  
  129. function fixme(name,client)
  130.     client:append( "FIXME: unimplemented command `"..name.."'." )
  131. end
  132.  
  133. function logout(name,client)
  134.     if client.type == host.client_type.net then
  135.         client:send("Bye-bye!")
  136.         client:del()
  137.     else
  138.         client:append("Error: Can't logout of stdin/stdout. Use quit or shutdown to close VLC.")
  139.     end
  140. end
  141.  
  142. function shutdown(name,client)
  143.     client:append("Bye-bye!")
  144.     h:broadcast("Shutting down.")
  145.     vlc.msg.info("Requested shutdown.")
  146.     vlc.misc.quit()
  147. end
  148.  
  149. function quit(name,client)
  150.     if client.type == host.client_type.net then
  151.         logout(name,client)
  152.     else
  153.         shutdown(name,client)
  154.     end
  155. end
  156.  
  157. function add(name,client,arg)
  158.     -- TODO: par single and double quotes properly
  159.     local f
  160.     if name == "enqueue" then
  161.         f = vlc.playlist.enqueue
  162.     else
  163.         f = vlc.playlist.add
  164.     end
  165.     local options = {}
  166.     for o in string.gmatch(arg," +:([^ ]*)") do
  167.         table.insert(options,o)
  168.     end
  169.     arg = string.gsub(arg," +:.*$","")
  170.     f({{path=arg,options=options}})
  171. end
  172.  
  173. function playlist_is_tree( client )
  174.     if client.env.flatplaylist == 0 then
  175.         return true
  176.     else
  177.         return false
  178.     end
  179. end
  180.  
  181. function playlist(name,client,arg)
  182.     function playlist0(item,prefix)
  183.         local prefix = prefix or ""
  184.         if not item.flags.disabled then
  185.             local str = "| "..prefix..tostring(item.id).." - "..item.name
  186.             if item.duration > 0 then
  187.                 str = str.." ("..common.durationtostring(item.duration)..")"
  188.             end
  189.             if item.nb_played > 0 then
  190.                 str = str.." [played "..tostring(item.nb_played).." time"
  191.                 if item.nb_played > 1 then
  192.                     str = str .. "s"
  193.                 end
  194.                 str = str .. "]"
  195.             end
  196.             client:append(str)
  197.         end
  198.         if item.children then
  199.             for _, c in ipairs(item.children) do
  200.                 playlist0(c,prefix.."  ")
  201.             end
  202.         end
  203.     end
  204.     local playlist
  205.     local tree = playlist_is_tree(client)
  206.     if name == "search" then
  207.         playlist = vlc.playlist.search(arg or "", tree)
  208.     else
  209.         if tonumber(arg) then
  210.             playlist = vlc.playlist.get(tonumber(arg), tree)
  211.         elseif arg then
  212.             playlist = vlc.playlist.get(arg, tree)
  213.         else
  214.             playlist = vlc.playlist.get(nil, tree)
  215.         end
  216.     end
  217.     if name == "search" then
  218.         client:append("+----[ Search - "..(arg or "`reset'").." ]")
  219.     else
  220.         client:append("+----[ Playlist - "..playlist.name.." ]")
  221.     end
  222.     if playlist.children then
  223.         for _, item in ipairs(playlist.children) do
  224.             playlist0(item)
  225.         end
  226.     else
  227.         playlist0(playlist)
  228.     end
  229.     if name == "search" then
  230.         client:append("+----[ End of search - Use `search' to reset┬á]")
  231.     else
  232.         client:append("+----[ End of playlist ]")
  233.     end
  234. end
  235.  
  236. function playlist_sort(name,client,arg)
  237.     if not arg then
  238.         client:append("Valid sort keys are: id, title, artist, genre, random, duration, album.")
  239.     else
  240.         local tree = playlist_is_tree(client)
  241.         vlc.playlist.sort(arg,false,tree)
  242.     end
  243. end
  244.  
  245. function services_discovery(name,client,arg)
  246.     if arg then
  247.         if vlc.sd.is_loaded(arg) then
  248.             vlc.sd.remove(arg)
  249.             client:append(arg.." disabled.")
  250.         else
  251.             vlc.sd.add(arg)
  252.             client:append(arg.." enabled.")
  253.         end
  254.     else
  255.         local sd = vlc.sd.get_services_names()
  256.         client:append("+----[ Services discovery ]")
  257.         for n,ln in pairs(sd) do
  258.             local status
  259.             if vlc.sd.is_loaded(n) then
  260.                 status = "enabled"
  261.             else
  262.                 status = "disabled"
  263.             end
  264.             client:append("| "..n..": " .. ln .. " (" .. status .. ")")
  265.         end
  266.         client:append("+----[ End of services discovery ]")
  267.     end
  268. end
  269.  
  270. function print_text(label,text)
  271.     return function(name,client)
  272.         client:append("+----[ "..label.." ]")
  273.         client:append "|"
  274.         for line in string.gmatch(text,".-\r?\n") do
  275.             client:append("| "..string.gsub(line,"\r?\n",""))
  276.         end
  277.         client:append "|"
  278.         client:append("+----[ End of "..string.lower(label).." ]")
  279.     end
  280. end
  281.  
  282. function help(name,client,arg)
  283.     local width = client.env.width
  284.     local long = (name == "longhelp")
  285.     local extra = ""
  286.     if arg then extra = "matching `" .. arg .. "' " end
  287.     client:append("+----[ Remote control commands "..extra.."]")
  288.     for i, cmd in ipairs(commands_ordered) do
  289.         if (cmd == "" or not commands[cmd].adv or long)
  290.         and (not arg or string.match(cmd,arg)) then
  291.             local str = "| " .. cmd
  292.             if cmd ~= "" then
  293.                 local val = commands[cmd]
  294.                 if val.aliases then
  295.                     for _,a in ipairs(val.aliases) do
  296.                         str = str .. ", " .. a
  297.                     end
  298.                 end
  299.                 if val.args then str = str .. " " .. val.args end
  300.                 if #str%2 == 1 then str = str .. " " end
  301.                 str = str .. string.rep(" .",(width-(#str+#val.help)-1)/2)
  302.                 str = str .. string.rep(" ",width-#str-#val.help) .. val.help
  303.             end
  304.             client:append(str)
  305.         end
  306.     end
  307.     client:append("+----[ end of help ]")
  308. end
  309.  
  310. function input_info(name,client)
  311.     local categories = vlc.input_info()
  312.     for cat, infos in pairs(categories) do
  313.         client:append("+----[ "..cat.." ]")
  314.         client:append("|")
  315.         for name, value in pairs(infos) do
  316.             client:append("| "..name..": "..value)
  317.         end
  318.         client:append("|")
  319.     end
  320.     client:append("+----[ end of stream info ]")
  321. end
  322.  
  323. function playlist_status(name,client)
  324.     local a,b,c = vlc.playlist.status()
  325.     client:append( "( new input: " .. tostring(a) .. " )" )
  326.     client:append( "( audio volume: " .. tostring(b) .. " )")
  327.     client:append( "( state " .. tostring(c) .. " )")
  328. end
  329.  
  330. function is_playing(name,client)
  331.     if vlc.input.is_playing() then client:append "1" else client:append "0" end
  332. end
  333.  
  334. function ret_print(foo,start,stop)
  335.     local start = start or ""
  336.     local stop = stop or ""
  337.     return function(discard,client,...) client:append(start..tostring(foo(...))..stop) end
  338. end
  339.  
  340. function get_time(var)
  341.     return function(name,client)
  342.         local input = vlc.object.input()
  343.         client:append(math.floor(vlc.var.get( input, var )))
  344.     end
  345. end
  346.  
  347. function titlechap(name,client,value)
  348.     local input = vlc.object.input()
  349.     local var = string.gsub( name, "_.*$", "" )
  350.     if value then
  351.         vlc.var.set( input, var, value )
  352.     else
  353.         local item = vlc.var.get( input, var )
  354.         -- Todo: add item name conversion
  355.         client:append(item)
  356.     end
  357. end
  358. function titlechap_offset(client,offset)
  359.     return function(name,value)
  360.         local input = vlc.object.input()
  361.         local var = string.gsub( name, "_.*$", "" )
  362.         vlc.var.set( input, var, vlc.var.get( input, var )+offset )
  363.     end
  364. end
  365.  
  366. function seek(name,client,value)
  367.     common.seek(value)
  368. end
  369.  
  370. function volume(name,client,value)
  371.     if value then
  372.         vlc.volume.set(value)
  373.     else
  374.         client:append(tostring(vlc.volume.get()))
  375.     end
  376. end
  377.  
  378. function rate(name,client)
  379.     local input = vlc.object.input()
  380.     if name == "normal" then
  381.         vlc.var.set(input,"rate",1000) -- FIXME: INPUT_RATE_DEFAULT
  382.     else
  383.         vlc.var.set(input,"rate-"..name,nil)
  384.     end
  385. end
  386.  
  387. function listvalue(obj,var)
  388.     return function(client,value)
  389.         local o = vlc.object.find(nil,obj,"anywhere")
  390.         if not o then return end
  391.         if value then
  392.             vlc.var.set( o, var, value )
  393.         else
  394.             local c = vlc.var.get( o, var )
  395.             local v, l = vlc.var.get_list( o, var )
  396.             client:append("+----[ "..var.." ]")
  397.             for i,val in ipairs(v) do
  398.                 local mark = (val==c)and " *" or ""
  399.                 client:append("| "..tostring(val).." - "..tostring(l[i])..mark)
  400.             end
  401.             client:append("+----[ end of "..var.." ]")
  402.         end
  403.     end
  404. end
  405.  
  406. function eval(client,val)
  407.     client:append(loadstring("return "..val)())
  408. end
  409.  
  410. --[[┬áDeclare commands, register their callback functions and provide
  411.      help strings here.
  412.      Syntax is:
  413.      "<command name>"; { func = <function>; [ args = "<str>"; ] help = "<str>"; [ adv = <bool>; ] [ aliases = { ["<str>";]* }; ] }
  414.      ]]
  415. commands_ordered = {
  416.     { "add"; { func = add; args = "XYZ"; help = "add XYZ to playlist" } };
  417.     { "enqueue"; { func = add; args = "XYZ"; help = "queue XYZ to playlist" } };
  418.     { "playlist"; { func = playlist; help = "show items currently in playlist" } };
  419.     { "search"; { func = playlist; args = "[string]"; help = "search for items in playlist (or reset search)" } };
  420.     { "sort"; { func = playlist_sort; args = "key"; help = "sort the playlist" } };
  421.     { "sd"; { func = services_discovery; args = "[sd]"; help = "show services discovery or toggle" } };
  422.     { "play"; { func = skip2(vlc.playlist.play); help = "play stream" } };
  423.     { "stop"; { func = skip2(vlc.playlist.stop); help = "stop stream" } };
  424.     { "next"; { func = skip2(vlc.playlist.next); help = "next playlist item" } };
  425.     { "prev"; { func = skip2(vlc.playlist.prev); help = "previous playlist item" } };
  426.     { "goto"; { func = skip2(vlc.playlist.goto); help = "goto item at index" } };
  427.     { "repeat"; { func = skip2(vlc.playlist.repeat_); args = "[on|off]"; help = "toggle playlist repeat" } };
  428.     { "loop"; { func = skip2(vlc.playlist.loop); args = "[on|off]"; help = "toggle playlist loop" } };
  429.     { "random"; { func = skip2(vlc.playlist.random); args = "[on|off]"; help = "toggle playlist random" } };
  430.     { "clear"; { func = skip2(vlc.playlist.clear); help = "clear the playlist" } };
  431.     { "status"; { func = playlist_status; help = "current playlist status" } };
  432.     { "title"; { func = titlechap; args = "[X]"; help = "set/get title in current item" } };
  433.     { "title_n"; { func = titlechap_offset(1); help = "next title in current item" } };
  434.     { "title_p"; { func = titlechap_offset(-1); help = "previous title in current item" } };
  435.     { "chapter"; { func = titlechap; args = "[X]"; help = "set/get chapter in current item" } };
  436.     { "chapter_n"; { func = titlechap_offset(1); help = "next chapter in current item" } };
  437.     { "chapter_p"; { func = titlechap_offset(-1); help = "previous chapter in current item" } };
  438.     { "" };
  439.     { "seek"; { func = seek; args = "X"; help = "seek in seconds, for instance `seek 12'" } };
  440.     { "pause"; { func = setarg(common.hotkey,"key-play-pause"); help = "toggle pause" } };
  441.     { "fastforward"; { func = setarg(common.hotkey,"key-jump+extrashort"); help = "set to maximum rate" } };
  442.     { "rewind"; { func = setarg(common.hotkey,"key-jump-extrashort"); help = "set to minimum rate" } };
  443.     { "faster"; { func = rate; help = "faster playing of stream" } };
  444.     { "slower"; { func = rate; help = "slower playing of stream" } };
  445.     { "normal"; { func = rate; help = "normal playing of stream" } };
  446.     { "fullscreen"; { func = skip2(vlc.video.fullscreen); args = "[on|off]"; help = "toggle fullscreen"; aliases = { "f", "F" } } };
  447.     { "info"; { func = input_info; help = "information about the current stream" } };
  448.     { "get_time"; { func = get_time("time"); help = "seconds elapsed since stream's beginning" } };
  449.     { "is_playing"; { func = is_playing; help = "1 if a stream plays, 0 otherwise" } };
  450.     { "get_title"; { func = ret_print(vlc.input.get_title); help = "the title of the current stream" } };
  451.     { "get_length"; { func = get_time("length"); help = "the length of the current stream" } };
  452.     { "" };
  453.     { "volume"; { func = volume; args = "[X]"; help = "set/get audio volume" } };
  454.     { "volup"; { func = ret_print(vlc.volume.up,"( audio volume: "," )"); args = "[X]"; help = "raise audio volume X steps" } };
  455.     { "voldown"; { func = ret_print(vlc.volume.down,"( audio volume: "," )"); args = "[X]"; help = "lower audio volume X steps" } };
  456.     { "adev"; { func = skip(listvalue("aout","audio-device")); args = "[X]"; help = "set/get audio device" } };
  457.     { "achan"; { func = skip(listvalue("aout","audio-channels")); args = "[X]"; help = "set/get audio channels" } };
  458.     { "atrack"; { func = skip(listvalue("input","audio-es")); args = "[X]"; help = "set/get audio track" } };
  459.     { "vtrack"; { func = skip(listvalue("input","video-es")); args = "[X]"; help = "set/get video track" } };
  460.     { "vratio"; { func = skip(listvalue("vout","aspect-ratio")); args = "[X]"; help = "set/get video aspect ratio" } };
  461.     { "vcrop"; { func = skip(listvalue("vout","crop")); args = "[X]"; help = "set/get video crop"; aliases = { "crop" } } };
  462.     { "vzoom"; { func = skip(listvalue("vout","zoom")); args = "[X]"; help = "set/get video zoom"; aliases = { "zoom" } } };
  463.     { "snapshot"; { func = common.snapshot; help = "take video snapshot" } };
  464.     { "strack"; { func = skip(listvalue("input","spu-es")); args = "[X]"; help = "set/get subtitles track" } };
  465.     { "hotkey"; { func = skip(common.hotkey); args = "[hotkey name]"; help = "simulate hotkey press"; adv = true; aliases = { "key" } } };
  466.     { "menu"; { func = fixme; args = "[on|off|up|down|left|right|select]"; help = "use menu"; adv = true } };
  467.     { "" };
  468.     { "set"; { func = set_env; args = "[var [value]]"; help = "set/get env var"; adv = true } };
  469.     { "save_env"; { func = save_env; help = "save env vars (for future clients)"; adv = true } };
  470.     { "alias"; { func = skip(alias); args = "[cmd]"; help = "set/get command aliases"; adv = true } };
  471.     { "eval"; { func = skip(eval); help = "eval some lua (*debug*)"; adv =true } }; -- FIXME: comment out if you're not debugging
  472.     { "description"; { func = print_text("Description",description); help = "describe this module" } };
  473.     { "license"; { func = print_text("License message",vlc.misc.license()); help = "print VLC's license message"; adv = true } };
  474.     { "help"; { func = help; args = "[pattern]"; help = "a help message"; aliases = { "?" } } };
  475.     { "longhelp"; { func = help; args = "[pattern]"; help = "a longer help message" } };
  476.     { "logout"; { func = logout; help = "exit (if in a socket connection)" } };
  477.     { "quit"; { func = quit; help = "quit VLC (or logout if in a socket connection)" } };
  478.     { "shutdown"; { func = shutdown; help = "shutdown VLC" } };
  479.     }
  480. commands = {}
  481. for i, cmd in ipairs( commands_ordered ) do
  482.     if #cmd == 2 then
  483.         commands[cmd[1]]=cmd[2]
  484.         if cmd[2].aliases then
  485.             for _,a in ipairs(cmd[2].aliases) do
  486.                 commands[a]=cmd[1]
  487.             end
  488.         end
  489.     end
  490.     commands_ordered[i]=cmd[1]
  491. end
  492. --[[ From now on commands_ordered is a list of the different command names
  493.      and commands is a associative array indexed by the command name. ]]
  494.  
  495. -- Compute the column width used when printing a the autocompletion list
  496. env.colwidth = 0
  497. for c,_ in pairs(commands) do
  498.     if #c > env.colwidth then env.colwidth = #c end
  499. end
  500. env.coldwidth = env.colwidth + 1
  501.  
  502. -- Count unimplemented functions
  503. do
  504.     local count = 0
  505.     local list = "("
  506.     for c,v in pairs(commands) do
  507.         if v.func == fixme then
  508.             count = count + 1
  509.             if count ~= 1 then
  510.                 list = list..","
  511.             end
  512.             list = list..c
  513.         end
  514.     end
  515.     list = list..")"
  516.     if count ~= 0 and env.welcome then
  517.         env.welcome = env.welcome .. "\r\nWarning: "..count.." functions are still unimplemented "..list.."."
  518.     end
  519. end
  520.  
  521. --[[ Utils ]]
  522. function split_input(input)
  523.     local input = strip(input)
  524.     local s = string.find(input," ")
  525.     if s then
  526.         return string.sub(input,0,s-1), strip(string.sub(input,s))
  527.     else
  528.         return input
  529.     end
  530. end
  531.  
  532. function call_command(cmd,client,arg)
  533.     if type(commands[cmd]) == type("") then
  534.         cmd = commands[cmd]
  535.     end
  536.     local ok, msg
  537.     if arg ~= nil then
  538.         ok, msg = pcall( commands[cmd].func, cmd, client, arg )
  539.     else
  540.         ok, msg = pcall( commands[cmd].func, cmd, client )
  541.     end
  542.     if not ok then
  543.         local a = arg or ""
  544.         if a ~= "" then a = " " .. a end
  545.         client:append("Error in `"..cmd..a.."' ".. msg)
  546.     end
  547. end
  548.  
  549. function call_libvlc_command(cmd,client,arg)
  550.     local ok, vlcerr, vlcmsg = pcall( vlc.var.libvlc_command, cmd, arg )
  551.     if not ok then
  552.         local a = arg or ""
  553.         if a ~= "" then a = " " .. a end
  554.         client:append("Error in `"..cmd..a.."' ".. vlcerr) -- when pcall fails, the 2nd arg is the error message.
  555.     end
  556.     return vlcerr
  557. end
  558.  
  559. function call_object_command(cmd,client,arg)
  560.     local var, val = split_input(arg)
  561.     local ok, vlcmsg, vlcerr, vlcerrmsg = pcall( vlc.var.command, cmd, var, val )
  562.     if not ok then
  563.         client:append("Error in `"..cmd.." "..var.." "..val.."' ".. vlcmsg) -- when pcall fails the 2nd arg is the error message
  564.     end
  565.     if vlcmsg ~= "" then
  566.         client:append(vlcmsg)
  567.     end
  568.     return vlcerr
  569. end
  570.  
  571. --[[┬áSetup host ]]
  572. require("host")
  573. h = host.host()
  574. -- No auth
  575. h.status_callbacks[host.status.password] = function(client)
  576.     client.env = common.table_copy( env )
  577.     client:send( client.env.welcome .. "\r\n")
  578.     client:switch_status(host.status.read)
  579. end
  580. -- Print prompt when switching a client's status to `read'
  581. h.status_callbacks[host.status.read] = function(client)
  582.     client:send( client.env.prompt )
  583. end
  584.  
  585. h:listen( config.hosts or config.host or "*console" )
  586.  
  587. --[[ The main loop ]]
  588. while not vlc.misc.should_die() do
  589.     h:accept()
  590.     local write, read = h:select(0.1)
  591.  
  592.     for _, client in pairs(write) do
  593.         local len = client:send()
  594.         client.buffer = string.sub(client.buffer,len+1)
  595.         if client.buffer == "" then client:switch_status(host.status.read) end
  596.     end
  597.  
  598.     for _, client in pairs(read) do
  599.         local input = client:recv(1000)
  600.         local done = false
  601.         if string.match(input,"\n$") then
  602.             client.buffer = string.gsub(client.buffer..input,"\r?\n$","")
  603.             done = true
  604.         elseif client.buffer == ""
  605.            and ((client.type == host.client_type.stdio and input == "")
  606.            or  (client.type == host.client_type.net and input == "\004")) then
  607.             -- Caught a ^D
  608.             client.buffer = "quit"
  609.             done = true
  610.         else
  611.             client.buffer = client.buffer .. input
  612.         end
  613.         if done then
  614.             local cmd,arg = split_input(client.buffer)
  615.             client.buffer = ""
  616.             client:switch_status(host.status.write)
  617.             if commands[cmd] then
  618.                 call_command(cmd,client,arg)
  619.             elseif string.sub(cmd,0,1)=='@'
  620.             and call_object_command(string.sub(cmd,2,#cmd),client,arg) == 0 then
  621.                 --
  622.             elseif client.type == host.client_type.stdio
  623.             and call_libvlc_command(cmd,client,arg) == 0 then
  624.                 --
  625.             else
  626.                 local choices = {}
  627.                 if client.env.autocompletion ~= 0 then
  628.                     for v,_ in common.pairs_sorted(commands) do
  629.                         if string.sub(v,0,#cmd)==cmd then
  630.                             table.insert(choices, v)
  631.                         end
  632.                     end
  633.                 end
  634.                 if #choices == 1 and client.env.autoalias ~= 0 then
  635.                     -- client:append("Aliasing to \""..choices[1].."\".")
  636.                     cmd = choices[1]
  637.                     call_command(cmd,client,arg)
  638.                 else
  639.                     client:append("Unknown command `"..cmd.."'. Type `help' for help.")
  640.                     if #choices ~= 0 then
  641.                         client:append("Possible choices are:")
  642.                         local cols = math.floor(client.env.width/(client.env.colwidth+1))
  643.                         local fmt = "%-"..client.env.colwidth.."s"
  644.                         for i = 1, #choices do
  645.                             choices[i] = string.format(fmt,choices[i])
  646.                         end
  647.                         for i = 1, #choices, cols do
  648.                             local j = i + cols - 1
  649.                             if j > #choices then j = #choices end
  650.                             client:append("  "..table.concat(choices," ",i,j))
  651.                         end
  652.                     end
  653.                 end
  654.             end
  655.         end
  656.     end
  657. end
  658.